package com.tgy.MyFlowable.workflow.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.BetweenFormater;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ctsi.core.common.api.Result;
import com.ctsi.core.common.exception.MarsException;
import com.ctsi.core.common.util.$;
import com.ctsi.core.common.util.PastTimeFormatUtil;
import com.ctsi.core.database.util.PageUtil;
import com.ctsi.system.entity.User;
import com.ctsi.system.service.IUserService;
import com.ctsi.workflow.dto.FlwInstanceDTO;
import com.ctsi.workflow.dto.FlwUserTaskVO;
import com.ctsi.workflow.enums.FlwInstanceExceptionEnum;
import com.ctsi.workflow.service.IFlwDefinitionService;
import com.ctsi.workflow.service.IFlwEndTaskService;
import com.ctsi.workflow.service.IFlwInstanceService;
import com.ctsi.workflow.util.BpmGraphicUtil;
import com.ctsi.workflow.vo.FlwCommentHistoryVO;
import com.ctsi.workflow.vo.FlwDefinitionVO;
import com.ctsi.workflow.vo.FlwInstanceVO;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.RequiredArgsConstructor;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Comment;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 流程实例控制器
 *
 * @version 1.0
 * @author: wang xiao xiang
 * @date: 2021/8/8 19:39
 */
@Service
@RequiredArgsConstructor
public class FlwInstanceServiceImpl implements IFlwInstanceService {

    private final String FORM_DATA_VARIABLE_NAME = "formData";

    /**
     * id
     */
    public final String ID = "id";

    /**
     * 名称
     */
    public final String NAME = "name";

    private final RepositoryService repositoryService;

    private final IFlwDefinitionService flwDefinitionService;

    private final HistoryService historyService;

    private final RuntimeService runtimeService;

    private final TaskService taskService;

    private final IFlwEndTaskService flwEndTaskService;

    @Autowired
    private IUserService userService;

    @Override
    public Page<FlwInstanceVO> page(FlwInstanceDTO flwInstanceDTO) {
        HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery();
        if (ObjectUtil.isNotNull(flwInstanceDTO)) {
            if (ObjectUtil.isNotEmpty(flwInstanceDTO.getStartUserId())) {
                //根据发起人id查询
                historicProcessInstanceQuery.startedBy(flwInstanceDTO.getStartUserId());
            }

            if (ObjectUtil.isNotEmpty(flwInstanceDTO.getCategory())) {
                //根据分类查询
                historicProcessInstanceQuery.processDefinitionCategory(flwInstanceDTO.getCategory());
            }

            if (ObjectUtil.isNotEmpty(flwInstanceDTO.getEnded())) {
                //根据状态查询
                if (flwInstanceDTO.getEnded()) {
                    historicProcessInstanceQuery.finished();
                } else {
                    historicProcessInstanceQuery.unfinished();
                }
            }

            //发起时间范围查询
            if (ObjectUtil.isAllNotEmpty(flwInstanceDTO.getStartDate(), flwInstanceDTO.getEndDate())) {
                DateTime beginTime = DateUtil.parseDateTime(flwInstanceDTO.getStartDate());
                DateTime endTime = DateUtil.parseDateTime(flwInstanceDTO.getEndDate());
                historicProcessInstanceQuery.startedAfter(beginTime).startedBefore(endTime);
            }
        }
        //根据流程实例发起时间倒序排列，最新发的在最上面
        historicProcessInstanceQuery.orderByProcessInstanceStartTime().desc();
        if (ObjectUtil.isNotEmpty(flwInstanceDTO.getKeyword())) {
            historicProcessInstanceQuery.processInstanceNameLike("%" + flwInstanceDTO.getKeyword()+ "%");
        }
        Page<FlwInstanceVO> defaultPage = PageUtil.getPage(flwInstanceDTO);
        long current = defaultPage.getCurrent();
        long size = defaultPage.getSize();
        List<HistoricProcessInstance> historicProcessInstanceList = historicProcessInstanceQuery
                .listPage(Convert.toInt((current - 1) * size), Convert.toInt(size));
        defaultPage.setTotal(historicProcessInstanceQuery.count());
        List<FlwInstanceVO> collect = historicProcessInstanceList.stream()
                .map(historicProcessInstance -> convertToVO(historicProcessInstance))
                .collect(Collectors.toList());
        defaultPage.setRecords(collect);
        return defaultPage;
    }

    @Override
    public List<FlwUserTaskVO> hisUserTask(FlwInstanceDTO flwInstanceDTO) {
        //校验流程实例是否存在
        HistoricProcessInstance historicProcessInstance = this.queryHistoricProcessInstance(flwInstanceDTO);
        String processInstanceId = historicProcessInstance.getId();
        //使用set去重
        Set<FlwUserTaskVO> resultSet = CollectionUtil.newHashSet();
        //定义任务key集合
        Set<String> definitionKeySet = CollectionUtil.newHashSet();
        //只查走过的
        HistoricTaskInstanceQuery historicTaskInstanceQuery = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId).finished().orderByHistoricTaskInstanceStartTime().desc();
        historicTaskInstanceQuery.list().forEach(historicTaskInstance -> {
            if (!definitionKeySet.contains(historicTaskInstance.getTaskDefinitionKey())) {
                FlwUserTaskVO flwUserTaskVO = new FlwUserTaskVO();
                flwUserTaskVO.setId(historicTaskInstance.getTaskDefinitionKey());
                flwUserTaskVO.setProcessDefinitionId(historicTaskInstance.getProcessDefinitionId());
                flwUserTaskVO.setName(historicTaskInstance.getName());
                if (ObjectUtil.isNotEmpty(historicTaskInstance.getAssignee())) {
                    flwUserTaskVO.setAssigneeName(userService.getById($.toLong(historicTaskInstance.getAssignee())).getAccount());
                }
                definitionKeySet.add(historicTaskInstance.getTaskDefinitionKey());
                resultSet.add(flwUserTaskVO);
            }
        });
        return CollectionUtil.newArrayList(resultSet);
    }

    @Override
    public JsonNode trace(FlwInstanceDTO flwInstanceDTO) {
        //校验流程实例是否存在
        HistoricProcessInstance historicProcessInstance = this.queryHistoricProcessInstance(flwInstanceDTO);
        String processInstanceId = historicProcessInstance.getId();
        //获取流程定义的BpmnModel
        BpmnModel pojoModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId());
        //不存在则该流程实例无定义
        if (ObjectUtil.hasEmpty(pojoModel, pojoModel.getLocationMap())) {
            throw new MarsException(FlwInstanceExceptionEnum.INSTANCE_HAS_NO_DEFINITION);
        }
        //获取实例历史节点
        List<HistoricActivityInstance> activityInstances = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).list();
        Set<String> completedActivityInstances = new HashSet<>();
        Set<String> currentActivityInstances = new HashSet<>();
        if ($.isNotEmpty(activityInstances)) {
            for (HistoricActivityInstance activityInstance : activityInstances) {
                if (activityInstance.getEndTime() != null) {
                    completedActivityInstances.add(activityInstance.getActivityId());
                } else {
                    currentActivityInstances.add(activityInstance.getActivityId());
                }
            }
        }
        return BpmGraphicUtil.processProcessElements(pojoModel, completedActivityInstances, currentActivityInstances, CollectionUtil.newArrayList());
    }

    @Override
    public void activeOrSuspend(FlwInstanceDTO flwInstanceDTO, boolean isSuspend) {
        //根据id获取流程定义
        String id = flwInstanceDTO.getId();
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(id).singleResult();
        if (ObjectUtil.isNull(processInstance)) {
            throw new MarsException(FlwInstanceExceptionEnum.INSTANCE_NOT_EXIST);
        }
        boolean suspended = processInstance.isSuspended();

        //如果要挂起且已经是挂起状态
        if (suspended && isSuspend) {
            throw new MarsException(FlwInstanceExceptionEnum.INSTANCE_SUSPEND);
        }

        //如果要激活且已经是激活状态
        if (!suspended && !isSuspend) {
            throw new MarsException(FlwInstanceExceptionEnum.INSTANCE_ACTIVE);
        }

        if (isSuspend) {
            //挂起
            runtimeService.suspendProcessInstanceById(id);
        } else {
            //激活
            runtimeService.activateProcessInstanceById(id);
        }
    }

    @Override
    public List<FlwCommentHistoryVO> commentHistory(FlwInstanceDTO flwInstanceDTO) {
        HistoricProcessInstance historicProcessInstance = this.queryHistoricProcessInstance(flwInstanceDTO);
        String processInstanceId = historicProcessInstance.getId();
        //查询历史任务，查询所有的
        List<HistoricTaskInstance> historicTaskInstanceList = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId).orderByHistoricTaskInstanceStartTime().asc().list();

        List<FlwCommentHistoryVO> collect = historicTaskInstanceList.stream().map(historicTaskInstance -> {
            FlwCommentHistoryVO flwCommentHistoryVO = $.copy(historicTaskInstance, FlwCommentHistoryVO.class);
            flwCommentHistoryVO.setTaskId(historicTaskInstance.getId());
            flwCommentHistoryVO.setActivityId(historicTaskInstance.getTaskDefinitionKey());
            if (ObjectUtil.isNotEmpty(historicTaskInstance.getAssignee())) {
                if ("INITIATOR".equals(historicTaskInstance.getAssignee())) {
                    flwCommentHistoryVO.setAssigneeInfo("处理人:" + historicTaskInstance.getAssignee());
                } else {
                    flwCommentHistoryVO.setAssigneeName(userService.getById(historicTaskInstance.getAssignee()).getAccount());
                    flwCommentHistoryVO.setAssigneeInfo("处理人:" + flwCommentHistoryVO.getAssigneeName());
                }
            } else {
                //任务办理人为空，即没有指定具体的人，则可能是候选人或候选组，则设置候选人或候选组名字
                if (ObjectUtil.isNotEmpty(historicTaskInstance.getAssignee())) {
                    flwCommentHistoryVO.setAssigneeName(userService.getById(historicTaskInstance.getAssignee()).getAccount());
                }else {
                    flwCommentHistoryVO.setAssigneeInfo("处理人:" + flwCommentHistoryVO.getAssigneeName());
                }
            }
            flwCommentHistoryVO.setDuration("-");
            if (ObjectUtil.isNotEmpty(historicTaskInstance.getDurationInMillis())) {
                flwCommentHistoryVO.setDuration(DateUtil.formatBetween(historicTaskInstance.getDurationInMillis(), BetweenFormater.Level.SECOND));
            }
            flwCommentHistoryVO.setComment(getTaskComments(historicTaskInstance.getId()));
            return flwCommentHistoryVO;
        }).collect(Collectors.toList());
        return collect;
    }

    @Override
    public void end(FlwInstanceDTO flwInstanceDTO) {
        HistoricProcessInstance historicProcessInstance = this.queryHistoricProcessInstance(flwInstanceDTO);
        String processInstanceId = historicProcessInstance.getId();

        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId).singleResult();
        if (ObjectUtil.isNull(processInstance)) {
            throw new MarsException(FlwInstanceExceptionEnum.INSTANCE_NOT_EXIST);
        }
        boolean suspended = processInstance.isSuspended();
        //流程挂起时无法结束
        if (suspended) {
            throw new MarsException(FlwInstanceExceptionEnum.INSTANCE_SUSPEND_CAN_NOT_END);
        }
        //查询当前流程中正在运行的任务
        List<Task> taskList = taskService.createTaskQuery().processInstanceId(processInstanceId).active().list();
        if (ObjectUtil.isNotEmpty(taskList)) {
            //给任务添加意见并结束
            Task task = taskList.get(0);
            String comment = flwInstanceDTO.getComment();
            flwEndTaskService.end(task.getId(), comment);
        }
    }

    @Override
    public void del(List<FlwInstanceDTO> flwInstanceDTOList) {
        flwInstanceDTOList.forEach(flwInstanceDTO -> {
            String processInstanceId = flwInstanceDTO.getId();
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                    .processInstanceId(processInstanceId).singleResult();
            if ($.isNotNull(processInstance)) {
                runtimeService.deleteProcessInstance(processInstanceId, null);
            }
            HistoricProcessInstance historicProcessInstance = this.queryHistoricProcessInstance(flwInstanceDTO);
            if ($.isNotEmpty(historicProcessInstance)) {
                historyService.deleteHistoricProcessInstance(processInstanceId);
            }
        });
    }

    @Override
    public Map<String, Object> formData(FlwInstanceDTO flwInstanceDTO) {
        Map<String, Object> variables = CollectionUtil.newHashMap();
        //获取流程实例id
        String processInstanceId = flwInstanceDTO.getId();

        //TODO 判断流程是否结束
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if (ObjectUtil.isNotEmpty(processInstance) && !processInstance.isEnded()) {
            //流程未结束
            variables = runtimeService.getVariables(processInstanceId);
        }else {
            //流程已结束
            //获取其历史变量集合
            List<HistoricVariableInstance> historicVariableInstanceList = historyService.createHistoricVariableInstanceQuery().processInstanceId(processInstanceId)
                    .list();
            /*historicVariableInstanceList.forEach(historicVariableInstance -> {
                if (FORM_DATA_VARIABLE_NAME.equals(historicVariableInstance.getVariableName())) {
                    variables.put(historicVariableInstance.getVariableName(), historicVariableInstance.getValue());
                }
            });*/
            variables = historicVariableInstanceList.stream()
                    .filter(o -> ObjectUtil.isNotEmpty(o.getValue()))
                    .collect(Collectors.toMap(HistoricVariableInstance :: getVariableName, HistoricVariableInstance :: getValue, (o1, o2) -> o2));
        }


        return variables;
    }

    @Override
    public List<Dict> addSignUserSelector(FlwInstanceDTO flwInstanceDTO) {
        List<Dict> resultList = CollectionUtil.newArrayList();
        List<Long> taskAssigneeList = CollectionUtil.newArrayList();
        HistoricProcessInstance historicProcessInstance = this.queryHistoricProcessInstance(flwInstanceDTO);
        //获取当前的任务节点结合
        List<Task> taskList = taskService.createTaskQuery().processInstanceId(historicProcessInstance.getId()).list();
        if (ObjectUtil.isNotNull(taskList)) {
            taskList.forEach(task -> {
                //获取任务待办人
                String assignee = task.getAssignee();
                if (ObjectUtil.isNotNull(assignee)) {
                    taskAssigneeList.add(Convert.toLong(assignee));
                }
            });
        }
//        List<User> list = userService.lambdaQuery().select(User::getId).list();
//        //获取系统所有用户id集合
//        List<Long> allUserIdList = list.stream().map(User::getId).collect(Collectors.toList());
//        //任务待办人集合不为空
//        if (ObjectUtil.isNotEmpty(taskAssigneeList)) {
//            //取差集
//            allUserIdList = (List<Long>) CollectionUtil.disjunction(taskAssigneeList, allUserIdList);
//        }

        //遍历并组合
        taskAssigneeList.forEach(userId -> {
            Dict dict = Dict.create();
            dict.put(ID, userId);
            dict.put(NAME, Result.data(userService.getById(userId)).getData().getAccount());
            resultList.add(dict);
        });
        return resultList;
    }

    @Override
    public List<Dict> delSignUserSelector(FlwInstanceDTO flwInstanceDTO) {
        List<Dict> resultList = CollectionUtil.newArrayList();
        List<Long> taskAssigneeList = CollectionUtil.newArrayList();
        HistoricProcessInstance historicProcessInstance = this.queryHistoricProcessInstance(flwInstanceDTO);
        //获取当前的任务节点结合
        List<Task> taskList = taskService.createTaskQuery().processInstanceId(historicProcessInstance.getId()).list();
        if ($.isNotNull(taskList)) {
            taskList.forEach(task -> {
                //获取任务待办人
                String assignee = task.getAssignee();
                if (ObjectUtil.isNotNull(assignee)) {
                    taskAssigneeList.add(Convert.toLong(assignee));
                }
            });
        }
        //任务待办人集合不为空
        if ($.isNotEmpty(taskAssigneeList)) {
            taskAssigneeList.forEach(userId -> {
                Dict dict = Dict.create();
                dict.put(ID, userId);
                dict.put(NAME, Result.data(userService.getById(userId)).getData().getAccount());
                resultList.add(dict);
            });
        }
        return resultList;
    }

    @Override
    public FlwInstanceVO detail(String processInstanceId) {
        FlwInstanceDTO flwInstanceDTO = new FlwInstanceDTO();
        flwInstanceDTO.setId(processInstanceId);
        HistoricProcessInstance historicProcessInstance = this.queryHistoricProcessInstance(flwInstanceDTO);
        return convertToVO(historicProcessInstance);
    }

    /**
     * 获取流程实例
     *
     * @param flwInstanceDTO 请求参数
     * @return 历史流程实例
     */
    private HistoricProcessInstance queryHistoricProcessInstance(FlwInstanceDTO flwInstanceDTO) {
        String id = flwInstanceDTO.getId();
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(id).singleResult();
        if (ObjectUtil.isEmpty(historicProcessInstance)) {
            throw new MarsException(FlwInstanceExceptionEnum.INSTANCE_NOT_EXIST);
        }
        return historicProcessInstance;

    }

    /**
     * 获取任务审批记录
     **/
    public String getTaskComments(String taskId) {
        //一条任务只有一条审批意见，此处查询多条合并为一条
        List<Comment> commentList = taskService.getTaskComments(taskId);
        if (ObjectUtil.isNotEmpty(commentList)) {
            StringBuilder comments = new StringBuilder();
            if (ObjectUtil.isNotEmpty(commentList)) {
                for (Comment comment : commentList) {
                    //倒序前追加，保证顺序
                    comments.insert(0, comment.getFullMessage());
                }
            }
            return comments.toString();
        }
        return "无";
    }

    private FlwInstanceVO convertToVO(HistoricProcessInstance historicProcessInstance) {
        FlwInstanceVO flwInstanceVO = $.copy(historicProcessInstance, FlwInstanceVO.class);
        flwInstanceVO.setFormatStartTime($.formatPastTime(historicProcessInstance.getStartTime()));
        //调试时存在申请人id为空的情况
        if ($.isNotEmpty(historicProcessInstance.getStartUserId())) {
            User user = userService.getById($.toLong(historicProcessInstance.getStartUserId()));
            if (ObjectUtil.isNotEmpty(user)) {
                flwInstanceVO.setStartUserName(Result.data(user).getData().getAccount());
            }
        }
        //此处采用判断结束时间是否为空来决定流程是否结束
        flwInstanceVO.setEnded($.isNotEmpty(historicProcessInstance.getEndTime()));
        flwInstanceVO.setEndTime(historicProcessInstance.getEndTime());
        //流程实例是否挂起，如果已经结束了，则肯定没挂起，如果没结束，则查询状态
        if (flwInstanceVO.getEnded()) {
            flwInstanceVO.setSuspended(false);
            flwInstanceVO.setFormatEndTime(PastTimeFormatUtil.formatPastTime(historicProcessInstance.getEndTime()));
        } else {
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                    .processInstanceId(flwInstanceVO.getId()).singleResult();
            //如果流程不在运行中，则没挂起
            if ($.isNull(processInstance)) {
                flwInstanceVO.setSuspended(false);
            } else {
                flwInstanceVO.setSuspended(processInstance.isSuspended());
            }
        }
        FlwDefinitionVO procDef = flwDefinitionService.detail(historicProcessInstance.getProcessDefinitionId());
        flwInstanceVO.setProcDef(procDef);
        return flwInstanceVO;
    }
}
